1 using System;
2 using UnityEngine;
3 using UnityStandardAssets.CrossPlatformInput;
4
5 namespace UnityStandardAssets.Characters.FirstPerson
6 {
7 [RequireComponent(typeof (Rigidbody))]
8 [RequireComponent(typeof (CapsuleCollider))]
9 public class RigidbodyFirstPersonController : MonoBehaviour
10 {
11 [Serializable]
12 public class MovementSettings
13 {
14 public float ForwardSpeed = 8.0f; // Speed when walking forward
15 public float BackwardSpeed = 4.0f; // Speed when walking backwards
16 public float StrafeSpeed = 4.0f; // Speed when walking sideways
17 public float RunMultiplier = 2.0f; // Speed when sprinting
18 public KeyCode RunKey = KeyCode.LeftShift;
19 public float JumpForce = 30f;
20 public AnimationCurve SlopeCurveModifier = new AnimationCurve(new Keyframe(-90.0f, 1.0f), new Keyframe(0.0f, 1.0f), new Keyframe(90.0f, 0.0f));
21 [HideInInspector] public float CurrentTargetSpeed = 8f;
22
23 #if !MOBILE_INPUT
24 private bool m_Running;
25 #endif
26
27 public void UpdateDesiredTargetSpeed(Vector2 input)
28 {
29 if (input == Vector2.zero) return;
30 if (input.x > 0 || input.x < 0)
31 {
32 //strafe
33 CurrentTargetSpeed = StrafeSpeed;
34 }
35 if (input.y < 0)
36 {
37 //backwards
38 CurrentTargetSpeed = BackwardSpeed;
39 }
40 if (input.y > 0)
41 {
42 //forwards
43 //handled last as if strafing and moving forward at the same time forwards speed should take precedence
44 CurrentTargetSpeed = ForwardSpeed;
45 }
46 #if !MOBILE_INPUT
47 if (Input.GetKey(RunKey))
48 {
49 CurrentTargetSpeed *= RunMultiplier;
50 m_Running = true;
51 }
52 else
53 {
54 m_Running = false;
55 }
56 #endif
57 }
58
59 #if !MOBILE_INPUT
60 public bool Running
61 {
62 get { return m_Running; }
63 }
64 #endif
65 }
66
67
68 [Serializable]
69 public class AdvancedSettings
70 {
71 public float groundCheckDistance = 0.01f; // distance for checking if the controller is grounded ( 0.01f seems to work best for this )
72 public float stickToGroundHelperDistance = 0.5f; // stops the character
73 public float slowDownRate = 20f; // rate at which the controller comes to a stop when there is no input
74 public bool airControl; // can the user control the direction that is being moved in the air
75 [Tooltip("set it to 0.1 or more if you get stuck in wall")]
76 public float shellOffset; //reduce the radius by that ratio to avoid getting stuck in wall (a value of 0.1f is nice)
77 }
78
79
80 public Camera cam;
81 public MovementSettings movementSettings = new MovementSettings();
82 public MouseLook mouseLook = new MouseLook();
83 public AdvancedSettings advancedSettings = new AdvancedSettings();
84
85
86 private Rigidbody m_RigidBody;
87 private CapsuleCollider m_Capsule;
88 private float m_YRotation;
89 private Vector3 m_GroundContactNormal;
90 private bool m_Jump, m_PreviouslyGrounded, m_Jumping, m_IsGrounded;
91
92 public float prev_y;
93 public float curr_y;
94 private float prev_x, prev_z;
95 private float curr_x, curr_z;
96
97 [HideInInspector] public float start_x;
98 [HideInInspector] public float start_y;
99 [HideInInspector] public float start_z;
100
101 public Vector3 Velocity
102 {
103 get { return m_RigidBody.velocity; }
104 }
105
106 public bool Grounded
107 {
108 get { return m_IsGrounded; }
109 }
110
111 public bool Jumping
112 {
113 get { return m_Jumping; }
114 }
115
116 public bool Running
117 {
118 get
119 {
120 #if !MOBILE_INPUT
121 return movementSettings.Running;
122 #else
123 return false;
124 #endif
125 }
126 }
127
128
129 private void Start()
130 {
131 m_RigidBody = GetComponent<Rigidbody>();
132 m_Capsule = GetComponent<CapsuleCollider>();
133 mouseLook.Init (transform, cam.transform);
134
135 prev_y = m_RigidBody.transform.position.y;
136 curr_y = prev_y;
137
138 // Get initial position
139 start_x = m_RigidBody.transform.position.x;
140 start_y = m_RigidBody.transform.position.y;
141 start_z = m_RigidBody.transform.position.z;
142
143 }
144
145
146 private void Update()
147 {
148 RotateView();
149
150 if (CrossPlatformInputManager.GetButtonDown("Jump") && !m_Jump)
151 {
152 m_Jump = true;
153 }
154
155 curr_y = m_RigidBody.transform.position.y;
156 curr_x = m_RigidBody.transform.position.x;
157 curr_z = m_RigidBody.transform.position.z;
158
159 if (curr_y > prev_y + .08)
160 {
161 // Trying to go uphill
162 transform.position = new Vector3(prev_x, prev_y, prev_z);
163
164 } else
165 {
166 prev_y = curr_y;
167 prev_x = curr_x;
168 prev_z = curr_z;
169 }
170
171
172 }
173
174
175 private void FixedUpdate()
176 {
177 GroundCheck();
178 Vector2 input = GetInput();
179
180 if ((Mathf.Abs(input.x) > float.Epsilon || Mathf.Abs(input.y) > float.Epsilon) && (advancedSettings.airControl || m_IsGrounded))
181 {
182 // always move along the camera forward as it is the direction that it being aimed at
183 Vector3 desiredMove = cam.transform.forward*input.y + cam.transform.right*input.x;
184 desiredMove = Vector3.ProjectOnPlane(desiredMove, m_GroundContactNormal).normalized;
185
186 desiredMove.x = desiredMove.x*movementSettings.CurrentTargetSpeed;
187 desiredMove.z = desiredMove.z*movementSettings.CurrentTargetSpeed;
188 desiredMove.y = desiredMove.y*movementSettings.CurrentTargetSpeed;
189 if (m_RigidBody.velocity.sqrMagnitude <
190 (movementSettings.CurrentTargetSpeed*movementSettings.CurrentTargetSpeed))
191 {
192 m_RigidBody.AddForce(desiredMove*SlopeMultiplier(), ForceMode.Impulse);
193 }
194 }
195
196 if (m_IsGrounded)
197 {
198 m_RigidBody.drag = 5f;
199
200 if (m_Jump)
201 {
202 m_RigidBody.drag = 0f;
203 m_RigidBody.velocity = new Vector3(m_RigidBody.velocity.x, 0f, m_RigidBody.velocity.z);
204 m_RigidBody.AddForce(new Vector3(0f, movementSettings.JumpForce, 0f), ForceMode.Impulse);
205 m_Jumping = true;
206 }
207
208 if (!m_Jumping && Mathf.Abs(input.x) < float.Epsilon && Mathf.Abs(input.y) < float.Epsilon && m_RigidBody.velocity.magnitude < 1f)
209 {
210 m_RigidBody.Sleep();
211 }
212 }
213 else
214 {
215 m_RigidBody.drag = 0f;
216 if (m_PreviouslyGrounded && !m_Jumping)
217 {
218 StickToGroundHelper();
219 }
220 }
221 m_Jump = false;
222 }
223
224
225 private float SlopeMultiplier()
226 {
227 float angle = Vector3.Angle(m_GroundContactNormal, Vector3.up);
228 return movementSettings.SlopeCurveModifier.Evaluate(angle);
229 }
230
231
232 private void StickToGroundHelper()
233 {
234 RaycastHit hitInfo;
235 if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
236 ((m_Capsule.height/2f) - m_Capsule.radius) +
237 advancedSettings.stickToGroundHelperDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
238 {
239 if (Mathf.Abs(Vector3.Angle(hitInfo.normal, Vector3.up)) < 85f)
240 {
241 m_RigidBody.velocity = Vector3.ProjectOnPlane(m_RigidBody.velocity, hitInfo.normal);
242 }
243 }
244 }
245
246
247 private Vector2 GetInput()
248 {
249
250 Vector2 input = new Vector2
251 {
252 x = CrossPlatformInputManager.GetAxis("Horizontal"),
253 y = CrossPlatformInputManager.GetAxis("Vertical")
254 };
255 //Debug.Log("y= " + input.y);
256
257 // ALWAYS MOVE FORWARD
258 input.y = (float).3;
259
260 movementSettings.UpdateDesiredTargetSpeed(input);
261 return input;
262 }
263
264
265 private void RotateView()
266 {
267 //avoids the mouse looking if the game is effectively paused
268 if (Mathf.Abs(Time.timeScale) < float.Epsilon) return;
269
270 // get the rotation before it's changed
271 // float oldYRotation = transform.eulerAngles.y;
272
273 mouseLook.LookRotation (transform, cam.transform);
274
275 /*
276
277 if (m_IsGrounded || advancedSettings.airControl)
278 {
279 // Rotate the rigidbody velocity to match the new direction that the character is looking
280 Quaternion velRotation = Quaternion.AngleAxis(transform.eulerAngles.y - oldYRotation, Vector3.up);
281 m_RigidBody.velocity = velRotation*m_RigidBody.velocity;
282 }
283 */
284 }
285
286 /// sphere cast down just beyond the bottom of the capsule to see if the capsule is colliding round the bottom
287 private void GroundCheck()
288 {
289 m_PreviouslyGrounded = m_IsGrounded;
290 RaycastHit hitInfo;
291 if (Physics.SphereCast(transform.position, m_Capsule.radius * (1.0f - advancedSettings.shellOffset), Vector3.down, out hitInfo,
292 ((m_Capsule.height/2f) - m_Capsule.radius) + advancedSettings.groundCheckDistance, Physics.AllLayers, QueryTriggerInteraction.Ignore))
293 {
294 m_IsGrounded = true;
295 m_GroundContactNormal = hitInfo.normal;
296 }
297 else
298 {
299 m_IsGrounded = false;
300 m_GroundContactNormal = Vector3.up;
301 }
302 if (!m_PreviouslyGrounded && m_IsGrounded && m_Jumping)
303 {
304 m_Jumping = false;
305 }
306 }
307
308
309 private void OnCollisionEnter(Collision collision)
310 {
311
312 //Debug.Log("Collision with " + collision.transform.gameObject.name);
313 //Debug.Log("x: " + start_x + " y: " + start_y + "z: " + start_z);
314
315 if (collision.gameObject.name.StartsWith("Pine_"))
316 {
317 Restart();
318 }
319
320 }
321
322
323 private void Restart()
324 {
325 transform.position = new Vector3(start_x, start_y, start_z);
326
327 prev_x = start_x;
328 prev_y = start_y;
329 prev_z = start_z;
330 }
331 }
332 }